/*
* ECARX Technology Limited is the owner of the copyright and the trade secret of this software.
* Without permission, no one has the right to obtain, disclose or use this software in any way.
*/

package com.ecarx.notificationx;

import android.app.Notification;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import android.util.Log;


public class NotificationControllerManager {

    private static final String TAG = NotificationControllerManager.class.getSimpleName();

    @NonNull
    private final Context context;

    public NotificationControllerManager(@NonNull Context context) {
        this.context = context;
    }

    @Nullable
    private static INotificationService notificationService;

    private static final ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Lg.d(TAG, "onServiceConnected: " + name);
            notificationService = INotificationService.Stub.asInterface(service);

            synchronized (LOCK) {
                LOCK.notifyAll();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Lg.d(TAG, "onServiceDisconnected: " + name);
            notificationService = null;
        }
    };

    private static boolean hasBind = false;

    private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());

    @AnyThread
    private void ensureBindService() {
        MAIN_HANDLER.post(new Runnable() {
            @Override
            public void run() {
                if (hasBind) {
                    return;
                }

                hasBind = true;
                Intent intent = new Intent();
                intent.setComponent(COMPONENT_NAME);
                Lg.d(TAG, "bindService");
                context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
            }
        });
    }

    private static final ComponentName COMPONENT_NAME = new ComponentName("com.ecarx.xsf.notificationcontroller", "ecarx.xsf.notificationcenter.NotificationService");

    @WorkerThread
    private INotificationService getNotificationService() {
        if (Thread.currentThread().equals(Looper.getMainLooper().getThread())) {
            throw new IllegalStateException("call this on work thread");
        }

        ensureServiceConnected();
        return notificationService;
    }

    @UiThread
    public void addListener(@NonNull final INotificationListener listener, @NonNull @NotificationHelper.CARD_TYPE_A final String type) {
        Intent intent = new Intent();
        intent.setComponent(COMPONENT_NAME);
        Lg.d(TAG, "bindService");
        context.bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Lg.d(TAG, "onServiceConnected");
                INotificationService notificationService = INotificationService.Stub.asInterface(service);
                try {
                    switch (type) {
                        case NotificationHelper.CardType.VOICE:
                            notificationService.addVoiceNotificationListener(listener);
                            break;
                        case NotificationHelper.CardType.LAUNCHER:
                            notificationService.addLauncherNotificationListener(listener);
                            break;
                        default:
                            break;
                    }
                } catch (Throwable throwable) {
                    Lg.w(TAG, "addListener error", throwable);
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Lg.d(TAG, "onServiceDisconnected");
            }
        }, Context.BIND_AUTO_CREATE);
    }

    private static final Object LOCK = new Object();

    @WorkerThread
    private void ensureServiceConnected() {
        ensureBindService();

        if (notificationService != null) {
            return;
        }

        Lg.d(TAG, "wait service connect start");
        synchronized (LOCK) {
            try {
                LOCK.wait(3 * 60 * 1000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error ", e);
            }
        }
        Lg.d(TAG, "wait service connect end");
    }

    private static final Handler WORK_HANDLE;

    static {
        HandlerThread handlerThread = new HandlerThread("notification work thread");
        handlerThread.start();
        WORK_HANDLE = new Handler(handlerThread.getLooper());
    }

    @AnyThread
    public void notify(final int id, final Notification notification) {
        WORK_HANDLE.post(new Runnable() {
            @Override
            public void run() {
                try {
                    getNotificationService().enqueueNotification(new StatusBarNotification(context.getPackageName(), id, notification, System.currentTimeMillis()));
                } catch (RemoteException e) {
                    Lg.e(TAG, "enqueueNotification", e);
                }
            }
        });
    }

    @AnyThread
    public void cancel(final int id) {
        WORK_HANDLE.post(new Runnable() {
            @Override
            public void run() {
                try {
                    getNotificationService().cancelNotification(context.getPackageName(), id);
                } catch (RemoteException e) {
                    Lg.e(TAG, "cancelNotification", e);
                }
            }
        });
    }

    @AnyThread
    public void notifyWidget(final int id, final ComponentName componentName, final Notification notification) {
        WORK_HANDLE.post(new Runnable() {
            @Override
            public void run() {
                try {
                    getNotificationService().enqueueNotification(new WidgetNotification(context.getPackageName(), id, notification, System.currentTimeMillis(), componentName));
                } catch (RemoteException e) {
                    Lg.e(TAG, "enqueueNotification", e);
                }
            }
        });
    }

}
